use crate::helper;
use regex::Regex;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::helper::{extract_combined_index, extract_data};

/// 网口信息，一般交换机，路由器，防火墙这些设备都有网口信息
#[derive(Debug, Serialize, Deserialize, Default, JsonSchema, Clone)]
pub struct NetworkInterface {
    /// 网口名称
    ///
    /// IF-MIB::ifName 1.3.6.1.2.1.31.1.1.1.1
    pub interface_name: String,
    /// 网口IP地址
    ///
    /// IP-MIB::ipAdEntAddr - 1.3.6.1.2.1.4.20.1.1
    ///
    /// IP-MIB::ipAdEntIfIndex - 1.3.6.1.2.1.4.20.1.2
    pub ipaddress: Option<String>,
    /// 网口mac地址
    ///
    /// IF-MIB::ifPhysAddress - 1.3.6.1.2.1.2.2.1.6
    pub mac_address: String,
    /// 网口连接状态，枚举值 true, false
    ///
    /// IF-MIB::ifConnectorPresent - 1.3.6.1.2.1.31.1.1.1.17 [false(2), true(1)]
    pub connect_result: bool,
    /// 出口与入口的流量和
    ///
    /// IF-MIB::ifHCOutOctets  + IF-MIB::ifHCInOctets
    pub in_and_out: String,
    /// 网口出口与入口的错误数的和
    ///
    /// IF-MIB::ifInErrors + IF-MIB::ifOutErrors  - 1.3.6.1.2.1.2.2.1.14 + 1.3.6.1.2.1.2.2.1.20
    pub in_and_out_errors: String,
    /// 网口最大网速
    ///
    /// Mb/s
    ///
    /// IF-MIB::ifHighSpeed  - 1.3.6.1.2.1.31.1.1.1.15
    pub max_speed: String,
    /// 网口最大传输单元
    ///
    /// IF-MIB::ifMtu - 1.3.6.1.2.1.2.2.1.4
    pub mtu: String,
    /// 网口描述
    ///
    /// IF-MIB::ifDescr - 1.3.6.1.2.1.2.2.1.2
    pub describe: String,
    /// 网口入站报错率
    ///
    /// IF-MIB::ifInErrors / IF-MIB::ifInOctets  -  1.3.6.1.2.1.2.2.1.14/1.3.6.1.2.1.2.2.1.10
    pub in_pkt_error_rate: String,
    /// 网口出站报错率
    ///
    /// IF-MIB::ifOutErrors / IF-MIB::ifOutOctets  - 1.3.6.1.2.1.2.2.1.20 / 1.3.6.1.2.1.2.2.1.16
    pub out_pkt_error_rate: String,
    /// 网口入口流量总计
    ///
    /// IF-MIB::ifHCInOctets - 1.3.6.1.2.1.31.1.1.1.6
    pub in_flux_count: String,
    /// 网口出口流量总计
    ///
    /// IF-MIB::ifHCOutOctets - 1.3.6.1.2.1.31.1.1.1.10
    pub out_flux_count: String,
    /// 网口物理状态
    ///
    /// ifOperStatus  .3.6.1.2.1.2.2.1.8  [down(2), up(1)]
    ///  up(1),        -- ready to pass packets
    /// down(2),
    /// testing(3),   -- in some test mode
    /// unknown(4),   -- status can not be determined
    ///               -- for some reason.
    /// dormant(5),
    /// notPresent(6),    -- some component is missing
    /// lowerLayerDown(7) -- down due to state of
    ///                   -- lower-layer interface(s)
    pub interface_status: String,
}

impl NetworkInterface {
    pub fn values(text: &str) -> Vec<Self> {
        let interface_re = Regex::new(r#".3.6.1.2.1.31.1.1.1.1.(\d+) = STRING: "(.+)""#).unwrap();
        let mut interfaces = vec![];
        let sys_uptime = helper::get_sys_uptime(text);
        for cap in interface_re.captures_iter(text) {
            interfaces.push(parse_interface_with_index(&cap[1], text, sys_uptime))
        }
        interfaces
    }
}

fn parse_interface_with_index(index: &str, value: &str, sys_uptime: f64) -> NetworkInterface {
    let out_count = extract_combined_index(
        ".3.6.1.2.1.31.1.1.1.10.",
        index,
        r" = Counter64: (\d+)",
        value,
    )
    .parse::<u64>()
    .unwrap_or(0);
    let in_count = extract_combined_index(
        ".3.6.1.2.1.31.1.1.1.6.",
        index,
        r" = Counter64: (\d+)",
        value,
    )
    .parse::<u64>()
    .unwrap_or(0);
    let in_error =
        extract_combined_index(".3.6.1.2.1.2.2.1.14.", index, r" = Counter32: (\d+)", value)
            .parse::<u64>()
            .unwrap_or(0);
    let out_error =
        extract_combined_index(".3.6.1.2.1.2.2.1.20.", index, r" = Counter32: (\d+)", value)
            .parse::<u64>()
            .unwrap_or(0);

    let ipaddress_str = extract_data(
        format!(r".3.6.1.2.1.4.20.1.2.(\d+\.\d+\.\d+\.\d+) = INTEGER: {index}").as_str(),
        value,
    );
    let ipaddress = if ipaddress_str.eq("") {
        None
    } else {
        Some(ipaddress_str)
    };
    let interface_status = match extract_combined_index(
        ".3.6.1.2.1.2.2.1.8.",
        index,
        r" = INTEGER: (\d+)",
        value,
    ).as_str() {
        "1" => "up(ready to pass packets)",
        "2" => "down",
        "3" => "testing(in some test mode)",
        "5" => "dormant",
        "6" => "notPresent(some component is missing)",
        "7" => "lowerLayerDown(down due to state of lower-layer interface[s])",
        _ => "unknown(status can not be determined for some reason)",
    };
    NetworkInterface {
        interface_name: extract_combined_index(
            ".3.6.1.2.1.31.1.1.1.1.",
            index,
            r#" = STRING: "(\S+)""#,
            value,
        ),
        ipaddress,
        mac_address: extract_combined_index(
            ".3.6.1.2.1.2.2.1.6.",
            index,
            r" = Hex-STRING: (.+)",
            value,
        ),
        in_and_out: format!("{:.4}", (in_count + out_count) as f64 / sys_uptime),
        in_and_out_errors: (in_error + out_error).to_string(),
        max_speed: extract_combined_index(
            ".3.6.1.2.1.31.1.1.1.15.",
            index,
            r" = Gauge32: (\d+)",
            value,
        ),
        mtu: extract_combined_index(".3.6.1.2.1.2.2.1.4.", index, r" = INTEGER: (\d+)", value),
        describe: extract_combined_index(
            ".3.6.1.2.1.2.2.1.2.",
            index,
            r#" = STRING: "(.+)""#,
            value,
        ),
        in_pkt_error_rate: if in_count > 0 {
            format!("{:.4}", (in_error as f64) / (in_count as f64))
        } else {
            "0".to_owned()
        },
        out_pkt_error_rate: if out_count > 0 {
            format!("{:.4}", (out_error as f64) / (out_count as f64))
        } else {
            "0".to_owned()
        },
        in_flux_count: extract_combined_index(
            ".3.6.1.2.1.31.1.1.1.6.",
            index,
            r" = Counter64: (\d+)",
            value,
        ),
        out_flux_count: extract_combined_index(
            ".3.6.1.2.1.31.1.1.1.10.",
            index,
            r" = Counter64: (\d+)",
            value,
        ),
        interface_status: interface_status.to_owned(),
        connect_result: extract_combined_index(
            ".3.6.1.2.1.31.1.1.1.17.",
            index,
            r" = INTEGER: (\d+)",
            value,
        ).contains('1'),
    }
}
